#pragma once
#include <brpc/channel.h>
#include <mutex>
#include <string>
#include <vector>
#include <unordered_map>

#include "logger.hpp"

//封装单个服务器的通道管理
class ServiceChannel
{
    public:
    using ptr = std::shared_ptr<ServiceChannel>;
    using ChannelPtr = std::shared_ptr<brpc::Channel>;
    
    ServiceChannel(const std::string& service_name) :_service_name(service_name),_index(0)
    {}

    //当服务上线后，添加通道
    void appendService(const std::string &host)
    {
        brpc::ChannelOptions options;
        // 设置连接超时时间，-1表示一直等待
        options.connect_timeout_ms = -1;
        // 设置等待超时时间，-1表示一直等待
        options.timeout_ms = -1;
        // 请求重试次数
        options.max_retry = 3;
        // 协议
        options.protocol = "baidu_std";

        auto channel = std::make_shared<brpc::Channel>(); 
        int ret = channel->Init(host.c_str(), &options);
        if (ret == -1)
        {
            LOG_ERROR("初始化通道失败{}:{}",_service_name,host);
            return;
        }
        std::unique_lock<std::mutex> lock(_mutex);
        _hosts.insert(std::make_pair(host,channel));
        _channels.push_back(channel);
    }

    //服务下线，从通道中删除
    void removeService(const std::string& host) 
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _hosts.find(host);
        if(it == _hosts.end()) {
            LOG_WARN("{}-{}删除节点的时候，没有找到对应的通道",_service_name,host);
            return;
        }

        for(auto vit = _channels.begin(); vit != _channels.end(); vit++) {
            if(*vit == it->second) {
                _channels.erase(vit);
            }
            break;
        }

        //从主机和通道的映射中删除掉
        _hosts.erase(host);
    }

    //RR轮转
    ChannelPtr RoundRobin()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if(_channels.size() == 0) 
        {
            LOG_ERROR("当前没有能提供{}服务的节点",_service_name);
            return ChannelPtr();
        }

        int32_t idx = (_index++) % _channels.size();
        return _channels[idx];
    }

private:
    std::mutex _mutex;
    // 服务名称
    std::string _service_name;
    // 采用RR轮转，下标
    int32_t _index;
    //通道的管理
    std::vector<ChannelPtr> _channels;
    //主机和通道的映射
    std::unordered_map<std::string,ChannelPtr> _hosts; 
};


//服务信息管理类
class ServiceManager 
{
    public:
    using ptr = std::shared_ptr<ServiceManager>;
    ServiceManager(){}

    //获取指定的服务ip和端口信息
    ServiceChannel::ChannelPtr choose(const std::string& service_name) {
        std::unique_lock<std::mutex> lock(_mutex);
        auto sit = _services.find(service_name);
        if(sit == _services.end())
        {
            LOG_ERROR("当前没有能提供{}服务的节点",service_name);
            return ServiceChannel::ChannelPtr();
        }

        return sit->second->RoundRobin();
    }


    //声明，关注哪些服务上下线，对他们进行管理
    void declared(const std::string& service_name) {
        std::unique_lock<std::mutex> lock(_mutex);
        _follow_service.insert(service_name);
    }

    //服务上线的时候，进行处理的函数,将节点管理起来
    void onServiceOnline(const std::string& service_instance,const std::string& host) 
    {
        std::string service_name = getServiceName(service_instance);
        ServiceChannel::ptr service;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto fit = _follow_service.find(service_name);
            if(fit == _follow_service.end()) {
                LOG_ERROR("{}:{} 服务上线了，但是当前不关心",service_name,host);
                return ; 
            }

            //获取管理的对象，没有就创建，有就添加到对应的通道中
            auto sit = _services.find(service_name);
            if(sit == _services.end()) {
                service = std::make_shared<ServiceChannel>(service_name);
                _services.insert(std::make_pair(service_name,service)); 
            }else {
                service = sit->second;
            }            
        } 

        if(!service) {
            LOG_ERROR("{}:{}服务上线新节点失败",service_name,host);
            return;
        }

        service->appendService(host);
        LOG_DEBUG("{}-{} 服务上线新节点，进行添加管理！", service_name, host);
    }

    void onServiceOffline(const std::string& service_instance,const std::string& host ){
        std::string service_name = getServiceName(service_instance);
        ServiceChannel::ptr service;
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto fit = _follow_service.find(service_name);
            if(fit == _follow_service.end()) {
                LOG_DEBUG("{}-{}服务下线了，但是不关心！",service_name,host);
                return;
            }

            auto sit = _services.find(service_name);
            if(sit == _services.end()) {
                LOG_WARN("删除{}服务节点时，没有找到通道对象", service_name);
                return;        
            }

            service = sit->second;
        }

        //下线通知
        service->removeService(host);
        LOG_DEBUG("{}-{}服务下线，将对应的通道删除",service_name,host);
    }


    private:
    // 提取出提供服务的名称
    std::string getServiceName(const std::string& service_instance) {
        auto pos = service_instance.find_last_of('/');
        if(pos == std::string::npos ) {
            return service_instance;
        }
        return service_instance.substr(0,pos);
    }

    private:
    std::mutex _mutex;
    //关注的服务
    std::unordered_set<std::string> _follow_service;
    std::unordered_map<std::string,ServiceChannel::ptr> _services;
};